#!/bin/bash

### Variable
image_dir=$1
gpio_name=${CPLD_GPIO}
cpld_file="${image_dir}/jtag.svf"
jtag="/dev/jtag0"
progress_file="/tmp/cpld_progress"
curr_version=""
update_version=""
firmware_type="CPLD"
soft_id=${image_dir##*/}
soft_dbus="xyz.openbmc_project.Software.BMC.Updater /xyz/openbmc_project/software/"

### Function Definition
#
# Verify version
verify_version() {
    curr_version=$(busctl get-property ${soft_dbus}cpld_active \
        "xyz.openbmc_project.Software.Version" "Version")
    curr_version=$(echo $curr_version | awk '{print $2}')

    update_version=$(busctl get-property ${soft_dbus}${soft_id} \
        "xyz.openbmc_project.Software.Version" "Version")
    update_version=$(echo $update_version | awk '{print $2}')
    if test "$curr_version" = "$update_version"; then
        echo "Update version $update_version is currently running on the host"
        return 1
    fi
}

# Check the power status of the host
check_chassis_state() {
    chassis_state=$(busctl get-property "xyz.openbmc_project.State.Chassis" "/xyz/openbmc_project/state/chassis0" \
        "xyz.openbmc_project.State.Chassis" "CurrentPowerState")

    if [[ $chassis_state =~ "On" ]]; then
        echo "The chassis is in a power on, can not update cpld"
        return 1
    fi
}

# Record failed messages to redfish log
generate_redfish_log() {
    openbmc_tag="OpenBMC.0.1.$1"
    para=""
    shift
    while [ $# -ne 0 ]; do
        para=$para",$1"
        shift
    done
    logger -p local7.info "${openbmc_tag}${para}"
}

## Delete firmware with failed updates
delete_software() {
    busctl call ${soft_dbus}${soft_id} "xyz.openbmc_project.Object.Delete" "Delete"
}

## Set software update state interface
## $1 state, it can be Staged Invalid Failed...
set_dbus_acvivate_state() {
    activate_state="xyz.openbmc_project.Software.Activation.Activations.$1"

    busctl set-property ${soft_dbus}${soft_id} "xyz.openbmc_project.Software.Activation" \
        "Activation" s $activate_state
}

##Set software update dbus progress info
## $1 Progress
set_dbus_progress_info() {
    local progress=$1
    busctl set-property ${soft_dbus}${soft_id} "xyz.openbmc_project.Software.ActivationProgress" \
        "Progress" y $progress
}

### When an error occurs, report logs, update status, delete software
handle_err() {
    local message=$1
    ### Send error log
    generate_redfish_log "FirmwareUpdateFailed" "$firmware_type" "$update_version" "$message"
    ### Set sctivate state failed
    set_dbus_acvivate_state "Failed"
    ### delete failed software
    delete_software
    exit 1
}

### Obtain progress information
find_str_progress() {
    local info=$1
    local key=$2
    str=$(echo "$info" | grep -i $key | tail -n 1)
    if test ! -z "$str"; then
        str=${str##*(}
        echo ${str%\%*}
    fi
}

### Check update status
# return
# 0 update
# 1 stop update
check_update_state() {
    local info=$1
    state=$(echo "$info" | grep "Done")
    if test -n "$state"; then
        generate_redfish_log "FirmwareUpdateCompleted" $firmware_type $update_version
        return 1
    fi

    state=$(echo "$info" | grep -iE "fail|not")
    if test -n "$state"; then
        handle_err "loadsvf program running error"
    fi
    return 0
}

### Main Process ####
generate_redfish_log "FirmwareUpdateStarted" $firmware_type $update_version
### Verify version information
verify_version
if test $? -ne 0; then
    handle_err "Same version"
fi

### Check if the chaiss is powered on
check_chassis_state
if test $? -ne 0; then
    handle_err "The chassis is in a power on state"
fi

### Check if the device or file exists
if test ! -e "$jtag"; then
    handle_err "jtag device does not exist"
fi

if test ! -e "$cpld_file"; then
    handle_err "jtag.svf does not exist"
fi
gpioutil set $gpio_name 1

# Set cpld update progress monitor
{
    set_progress=0
    while true; do
        if test ! -e $progress_file; then
            continue
        fi
        ##Get progress data output by loadsvf and remove '\r\n' characters.
        data=$(cat $progress_file | sed 's/\r/\n/g')

        # Exit the loop when encountering errors or completion
        check_update_state "$data"
        if test $? -ne 0; then
            break
        fi
        set_progress=$(find_str_progress "$data" "progress")

        ##Write the progress value to the DBUS interface.
        if test $set_progress; then
            set_dbus_progress_info $set_progress
        fi
        sleep 0.5
    done
} &

# Run cpld update
loadsvf -d /dev/jtag0 -v -s $cpld_file >$progress_file 2>&1
gpioutil set $gpio_name 0

# When the loadsvf program runs incorrectly, we need exit 1
data=$(cat $progress_file | sed 's/\r/\n/g')

state=$(echo "$data" | grep -iE "fail|not")
if test -n "$state"; then
    sleep 1
    rm $progress_file
    exit 1
fi
